Option 1: Just use Processes


In [33]:
import nengo
import numpy as np

class Steps(nengo.processes.Process):
    def __init__(self, width):
        self.width = width
    def make_step(self, size_in, size_out, dt, rng):
        last_index = np.array([-1])
        last_value = np.zeros(size_out)        
        
        def step(t):
            index = int(t / self.width)
            if index != last_index:
                last_value[:] = rng.uniform(-1, 1, size_out)
                last_index[:] = index
            return last_value
        return step
            
class Delay(nengo.processes.Process):
    def __init__(self, delay):
        self.delay = delay
    def make_step(self, size_in, size_out, dt, rng):
        timesteps = int(self.delay / dt)
        history = np.zeros((timesteps + 1, size_out))
        
        def step(t, x):
            history[:] = np.roll(history, -1)
            history[-1] = x
            return history[0]
        return step
    
model = nengo.Network()
with model:
    stim = nengo.Node(Steps(width=0.1), size_out=1)
    delay = nengo.Node(Delay(delay=0.05), size_in=1, size_out=1)
    nengo.Connection(stim, delay, synapse=None)
    probe1 = nengo.Probe(stim)
    probe2 = nengo.Probe(delay)
    
sim = nengo.Simulator(model, seed=1)
sim.run(1)
  
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()



In [34]:
# run the model again to make sure resetting works
sim = nengo.Simulator(model, seed=1)
sim.run(1)
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()
sim.reset()
sim.run(1)
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()


Option 2: Closures

  • since no one has a good name for these objects (other than NodeOutput), we don't give them a name
  • we detect that there's a "build" function and call it
  • we pass in the node, dt, and an initialized rng to the build function, it returns the step function

In [5]:
import nengo
import numpy as np

class Steps:
    def __init__(self, width):
        self.width = width
    def build(self, node, dt, rng):
        last_index = np.array([-1])
        last_value = np.zeros(node.size_out)        
        
        def step(t):
            index = int(t / self.width)
            if index != last_index:
                last_value[:] = rng.uniform(-1, 1, node.size_out)
                last_index[:] = index
            return last_value
        return step
            
class Delay:
    def __init__(self, delay):
        self.delay = delay
    def build(self, node, dt, rng):
        timesteps = int(self.delay / dt)
        history = np.zeros((timesteps + 1, node.size_out))
        
        def step(t, x):
            history[:] = np.roll(history, -1)
            history[-1] = x
            return history[0]
        return step
    
model = nengo.Network()
with model:
    stim = nengo.Node(Steps(width=0.1), size_out=1)
    delay = nengo.Node(Delay(delay=0.05), size_in=1, size_out=1)
    nengo.Connection(stim, delay, synapse=None)
    probe1 = nengo.Probe(stim)
    probe2 = nengo.Probe(delay)
    
sim = nengo.Simulator(model, seed=1)
sim.run(1)
  
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-cc27cbdc741b> in <module>()
     32 model = nengo.Network()
     33 with model:
---> 34     stim = nengo.Node(Steps(width=0.1), size_out=1)
     35     delay = nengo.Node(Delay(delay=0.05), size_in=1, size_out=1)
     36     nengo.Connection(stim, delay, synapse=None)

/home/tcstewar/github/nengo/nengo/base.pyc in __call__(cls, *args, **kwargs)
     22         add_to_container = kwargs.pop('add_to_container', True)
     23         # Do the __init__ before adding in case __init__ errors out
---> 24         inst.__init__(*args, **kwargs)
     25         if add_to_container:
     26             Network.add(inst)

/home/tcstewar/github/nengo/nengo/node.pyc in __init__(self, output, size_in, size_out, label)
    123         self.size_out = size_out
    124         self.label = label
--> 125         self.output = output  # Must be set after size_out; may modify size_out
    126 
    127     def __getitem__(self, key):

/home/tcstewar/github/nengo/nengo/base.pyc in __setattr__(self, name, val)
     61             val = Config.default(type(self), name)
     62         try:
---> 63             super(NengoObject, self).__setattr__(name, val)
     64         except Exception as e:
     65             arg0 = '' if len(e.args) == 0 else e.args[0]

/home/tcstewar/github/nengo/nengo/node.pyc in __set__(self, node, output)
     36             # Make into correctly shaped numpy array before validation
     37             output = npext.array(
---> 38                 output, min_dims=1, copy=False, dtype=np.float64)
     39             self.validate_ndarray(node, output)
     40             node.size_out = output.size

/home/tcstewar/github/nengo/nengo/utils/numpy.pyc in array(x, dims, min_dims, readonly, **kwargs)
     23 
     24 def array(x, dims=None, min_dims=0, readonly=False, **kwargs):
---> 25     y = np.array(x, **kwargs)
     26     dims = max(min_dims, y.ndim) if dims is None else dims
     27 

AttributeError: Validation error when setting 'Node.output': Steps instance has no attribute '__float__'

Option 3: shallow copy

  • if there is a build function, the builder makes a shallow copy and calls build
  • we use the __call__ method, since then it acts like a standard Python callable
  • we get to store variables with the standard self. approach.
  • but, the self that is used when running will never be the same instance as the one the user made
    • so if someone does print self for debugging purposes, they may get confused
    • we could set self.original to point to the original object

In [3]:
import nengo
import numpy as np

class Steps:
    def __init__(self, width):
        self.width = width
    def build(self, node, dt, rng):
        self.last_index = None
        self.last_value = np.zeros(node.size_out)
        self.rng = rng
    def __call__(self, t):
        index = int(t / self.width)
        if index != self.last_index:
            self.last_value = self.rng.uniform(-1, 1, self.last_value.shape)
            self.last_index = index
        return self.last_value
            
class Delay:
    def __init__(self, delay):
        self.delay = delay
    def build(self, node, dt, rng):
        timesteps = int(self.delay / dt)
        self.history = np.zeros((timesteps + 1, node.size_out))
    def __call__(self, t, x):
        self.history[:] = np.roll(self.history, -1)
        self.history[-1] = x
        return self.history[0]
    
model = nengo.Network()
with model:
    stim = nengo.Node(Steps(width=0.1), size_out=1)
    delay = nengo.Node(Delay(delay=0.05), size_in=1, size_out=1)
    nengo.Connection(stim, delay, synapse=None)
    probe1 = nengo.Probe(stim)
    probe2 = nengo.Probe(delay)
    
sim = nengo.Simulator(model, seed=1)
sim.run(1)
  
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()



In [4]:
# run the model again to make sure resetting works
sim = nengo.Simulator(model, seed=1)
sim.run(1)
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()
sim.reset()
sim.run(1)
plot(sim.trange(), sim.data[probe1])
plot(sim.trange(), sim.data[probe2])
show()



In [ ]: